package dipl.visualizer.graphics;

/// <summary>

import dipl.algorithm.math.fp.primitive.Matrix3x3f;
import dipl.algorithm.math.fp.primitive.Point2df;
import dipl.algorithm.math.fp.primitive.op.AffineTransformations2d;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Stroke;

/// Context for painting (wraps system context).
/// WARNING: not thread safe!
/// </summary>
public class PaintContext {

	//
	// CONSTRUCTOR METHODS
	//

	/// <summary>
	/// Construct paint context
	/// </summary>
	/// <param name="context">system context</param>
	public PaintContext( Graphics2D context ) {
		this.context = context;
		this.temp1 = new Point2df();
		this.temp2 = new Point2df();
	}

	//
	// PROPERTIES
	//
	
	/// <summary>
	/// Gets world transformation applied to all points before forwarded to system context
	/// </summary>
	public Matrix3x3f Transform() {
		return transform;
	}

		public void SetTransform( Matrix3x3f transform ) {
		this.transform = transform;
	}

	//
	// PUBLIC METHODS
	//
		
	/// <summary>
	/// Draws circle with given center and radius
	/// </summary>
	/// <param name="pen"></param>
	/// <param name="center"></param>
	/// <param name="radius"></param>
	public void DrawCircle( Point2df center, double radius ) {
		temp1.Set( center.x-radius, center.y+radius );
		temp2.Set( center.x+radius, center.y-radius );
		AffineTransformations2d.TransformPoint( transform, temp1 );
		AffineTransformations2d.TransformPoint( transform, temp2 );

		context.drawArc(
			(int)temp1.x, (int)temp1.y,
			(int)(temp2.x-temp1.x), (int)(temp2.y-temp1.y),
			0, 360 );
	}

	/// <summary>
	/// <see cref="Graphics.DrawLine"/>
	/// </summary>
	/// <param name="pen"></param>
	/// <param name="p1"></param>
	/// <param name="p2"></param>
	public void DrawLine( Point2df p1, Point2df p2 ) {
		temp1.Set( p1 );
		temp2.Set( p2 );
		AffineTransformations2d.TransformPoint( transform, temp1 );
		AffineTransformations2d.TransformPoint( transform, temp2 );
		context.drawLine( (int)temp1.x, (int)temp1.y, (int)temp2.x, (int)temp2.y );
	}

	/// <summary>
	/// <see cref="Graphics.DrawLine"/>
	/// </summary>
	/// <param name="pen"></param>
	/// <param name="x1"></param>
	/// <param name="y1"></param>
	/// <param name="x2"></param>
	/// <param name="y2"></param>
	public void DrawLine( double x1, double y1, double x2, double y2 ) {
		temp1.Set( x1, y1 );
		temp2.Set( x2, y2 );
		AffineTransformations2d.TransformPoint( transform, temp1 );
		AffineTransformations2d.TransformPoint( transform, temp2 );
		context.drawLine( (int)temp1.x, (int)temp1.y, (int)temp2.x, (int)temp2.y );
	}

	/// <summary>
	/// <see cref="Graphics.DrawString"/>
	/// </summary>
	/// <param name="s"></param>
	/// <param name="font"></param>
	/// <param name="brush"></param>
	/// <param name="x"></param>
	/// <param name="y"></param>
	public void DrawString( String s, double x, double y ) {
		temp1.Set( x, y );
		AffineTransformations2d.TransformPoint( transform, temp1 );
		context.drawString( s, (float)temp1.x, (float)temp1.y );
	}

	/// <summary>
	///
	/// </summary>
	/// <param name="brush"></param>
	/// <param name="origin"></param>
	/// <param name="radX"></param>
	/// <param name="radY"></param>
	/// <param name="angleStart">in degree</param>
	/// <param name="angleSweep">in degree</param>
	public void FillPie( Point2df origin, double radX, double radY, double angleStart, double angleSweep ) {
		temp1.Set( origin.x-radX, origin.y+radY );
		temp2.Set( origin.x+radX, origin.y-radY );
		AffineTransformations2d.TransformPoint( transform, temp1 );
		AffineTransformations2d.TransformPoint( transform, temp2 );
		
		context.fillArc( (int)temp1.x, (int)temp1.y, (int)(temp2.x-temp1.x), (int)(temp2.y-temp1.y), (int)angleStart, (int)angleSweep );
	}

	/// <summary>
	/// Fills given polygon using given brush
	/// </summary>
	/// <param name="brush"></param>
	/// <param name="points"></param>
	public void FillPolygon( Point2df[] points ) {
		int nPoints = points.length;
		int[] xPoints = new int[nPoints];
		int[] yPoints = new int[nPoints];
		for( int i= 0; i < nPoints; i++ ) {
			temp1.Set( points[i] );
			AffineTransformations2d.TransformPoint( transform, temp1 );
			xPoints[i] = (int)temp1.x;
			yPoints[i] = (int)temp1.y;
		}

		context.fillPolygon( xPoints, yPoints, nPoints );
	}

	/// <summary>
	/// <see cref="Graphics.FillRectangle"/>
	/// </summary>
	/// <param name="brush"></param>
	/// <param name="x"></param>
	/// <param name="y"></param>
	/// <param name="width"></param>
	/// <param name="height"></param>
	public void FillRectangle( double x, double y, double width, double height ) {
		temp1.Set( x, y );
		temp2.Set( x+width, y+height );
		AffineTransformations2d.TransformPoint( transform, temp1 );
		AffineTransformations2d.TransformPoint( transform, temp2 );
		context.fillRect( (int)temp1.x, (int)temp1.y, (int)Math.abs( temp2.x-temp1.x ), (int)Math.abs( temp2.y-temp1.y ) );
	}

	public void SetColor( Color color ) {
		context.setColor( color );
	}

	public void SetFont( Font font ) {
		context.setFont( font );
	}

	public void SetStroke( Stroke stroke ) {
		context.setStroke( stroke );
	}

	//
	// MEMBERS
	//

	/// <summary>
	/// wrapped windows graphics context
	/// </summary>
	private Graphics2D context;

	/// <summary>
	/// transformation applied to all points
	/// </summary>
	private Matrix3x3f transform;

	/// <summary>
	/// temporary point
	/// </summary>
	private Point2df temp1;

	/// <summary>
	/// temporary point
	/// </summary>
	private Point2df temp2;
}
